/*
  Losu Copyright Notice
  --------------------
    Losu is an open source programming language project under the MIT license
  that can be used for both academic and commercial purposes. There are no
  fees, no royalties, and no GNU-like restrictions. Losu qualifies
  as open source software. However, Losu is not public property, and founder
  'chen-chaochen' retains its copyright.

    Losu has been registered with the National Copyright Administration of the
  People's Republic of China, and adopts the MIT license as the copyright
  licensing contract under which the right holder conditionally licenses its
  reproduction, distribution, and modification rights to an unspecified public.

    If you use Losu, please follow the public MIT agreement or choose to enter
  into a dedicated license agreement with us.

  The MIT LICENSE is as follows
  --------------------
  Copyright  2020  chen-chaochen

    Permission is hereby granted, free of charge, to any person obtaining a
  copy of this software and associated documentation files (the “Software”), to
  deal in the Software without restriction, including without limitation the
  rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
  sell copies of the Software, and to permit persons to whom the Software is
  furnished to do so, subject to the following conditions:

    The above copyright notice and this permission notice shall be included in
  all copies or substantial portions of the Software.

    THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  SOFTWARE.
*/

#ifndef FILE_LIBS_PLATFS_C
#define FILE_LIBS_PLATFS_C
#define _GNU_SOURCE

#include "platfs.h"

// platfs is a platform-specific file system library
// this file is for unix-like systems

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <dirent.h>
#include <errno.h>
#include <limits.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>

#ifdef _WIN32
#define MKDIR(d, s) mkdir(d)
#else
#define MKDIR(d, s) mkdir(d, s)
#endif

/**
 * @brief remove file or directory recursively
 * @param path path to file or directory
 * @return 0 is success, -1 is fail
 */
losu_extern_t int32_t platfs_remove(const char* path) {
  struct stat path_stat;

  // 检查路径是否存在
  if (stat(path, &path_stat) != 0) {
    return 0;  // 路径不存在或无法访问
  }

  // 如果是普通文件或链接，直接删除
  if (!S_ISDIR(path_stat.st_mode)) {
    return unlink(path);
  }

  // 如果是目录，需要递归删除其内容
  DIR* dir = opendir(path);
  if (dir == NULL) {
    return -1;
  }

  struct dirent* entry;
  int result = 0;

  // 遍历目录中的所有项
  while ((entry = readdir(dir)) != NULL && result == 0) {
    // 跳过当前目录和父目录
    if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) {
      continue;
    }

    // 构造完整路径
    char* full_path;
    size_t path_len = strlen(path);
    size_t name_len = strlen(entry->d_name);

    full_path = malloc(path_len + name_len + 2);  // +1 for '/', +1 for '\0'
    if (full_path == NULL) {
      result = -1;
      break;
    }

    // 使用memcpy和snprintf替代strcpy和strcat
    memcpy(full_path, path, path_len);
    if (full_path[path_len - 1] != '/') {
      full_path[path_len] = '/';
      memcpy(full_path + path_len + 1, entry->d_name, name_len + 1);
    } else {
      memcpy(full_path + path_len, entry->d_name, name_len + 1);
    }

    // 递归删除子项
    result = platfs_remove(full_path);
    free(full_path);
  }

  closedir(dir);

  // 如果目录内容删除成功，删除目录本身
  if (result == 0) {
    result = rmdir(path);
  }

  return result;
}

/**
 * @brief rename file or directory
 * @param oldpath old path
 * @param newpath new path
 * @return 0 is success, -1 is fail
 */
losu_extern_t int32_t platfs_rename(const char* oldpath, const char* newpath) {
  return rename(oldpath, newpath);
}

/**
 * @brief mkdir path recursively
 * @param path path to directory
 * @return 0 is success, -1 is fail
 */
losu_extern_t int32_t platfs_mkdir(const char* path) {
  if (path == NULL) {
    return -1;
  }

  char* dir_path = strdup(path);
  if (dir_path == NULL) {
    return -1;
  }

  int result = 0;
  size_t len = strlen(dir_path);

  // 移除末尾的斜杠（如果不是根目录）
  if (len > 1 && dir_path[len - 1] == '/') {
    dir_path[len - 1] = '\0';
    len--;
  }

  // 逐级创建目录
  for (char* p = dir_path + 1; *p; p++) {
    if (*p == '/') {
      *p = '\0';  // 临时截断字符串

      // 创建当前级别的目录
      if (MKDIR(dir_path, 0755) != 0) {
        // 如果目录已存在则忽略错误，否则返回错误
        if (errno != EEXIST) {
          result = -1;
          break;
        }
      }

      *p = '/';  // 恢复字符串
    }
  }

  // 创建最终目录
  if (result == 0) {
    if (MKDIR(dir_path, 0755) != 0 && errno != EEXIST) {
      result = -1;
    }
  }

  free(dir_path);
  return result;
}

/**
 * @brief touch file, recursively
 * @param path path to file
 * @return 0 is success, -1 is fail
 */
losu_extern_t int32_t platfs_touch(const char* path) {
  char dir_path[PATH_MAX] = {0};
  if (platfs_path_dir(path, dir_path, sizeof(dir_path))) {
    return -1;
  }
  if (platfs_mkdir(dir_path))
    return -1;
  FILE* fp = fopen(path, "w");
  if (fp == NULL)
    return -1;
  fclose(fp);
  return 0;
}

/**
 * @brief check path is directory
 * @param path path to check
 * @return 0 is directory, -1 is file
 */
losu_extern_t int32_t platfs_isdir(const char* path) {
  struct stat path_stat;

  // 检查路径是否存在且可访问
  if (stat(path, &path_stat) != 0) {
    return -1;  // 路径不存在或无法访问
  }

  // 检查是否为目录
  if (S_ISDIR(path_stat.st_mode)) {
    return 0;  // 是目录
  }

  return -1;  // 不是目录
}

/**
 * @brief check path exsist
 * @param path path to check
 * @return 0 is exsist, -1 is not exsist
 */
losu_extern_t int32_t platfs_exsist(const char* path) {
  return access(path, F_OK);
}

/**
 * @brief get file modify time
 * @param path path to file
 * @return modify time
 */
losu_extern_t time_t platfs_mtime(const char* path) {
  struct stat path_stat;

  // 检查路径是否存在且可访问
  if (stat(path, &path_stat) != 0) {
    return -1;  // 路径不存在或无法访问
  }

  // 返回最后修改时间
  return path_stat.st_mtime;
}

/* get path name */
losu_extern_t int32_t platfs_path_name(const char* path,
                                       char* result,
                                       size_t size) {
  if (path == NULL || result == NULL) {
    return -1;
  }
  // 查找路径中最后一个 '/' 字符的位置
  const char* last_slash = strrchr(path, '/');

  const char* filename;
  if (last_slash != NULL) {
    // 如果找到了 '/'，则文件名是 '/' 后面的部分
    filename = last_slash + 1;
  } else {
    // 如果没有找到 '/'，则整个路径就是文件名
    filename = path;
  }

  // 计算文件名长度
  size_t filename_len = strlen(filename);
  if (filename_len >= size) {
    return -1;
  }
  // 使用安全的字符串复制函数
  snprintf(result, filename_len + 1, "%s", filename);
  return 0;
}

/* get path ext */
losu_extern_t int32_t platfs_path_ext(const char* path,
                                      char* result,
                                      size_t size) {
  if (path == NULL || result == NULL) {
    return -1;
  }

  // 首先获取文件名（不包含路径）
  const char* last_slash = strrchr(path, '/');
  const char* filename;
  if (last_slash != NULL) {
    filename = last_slash + 1;
  } else {
    filename = path;
  }

  // 查找文件名中最后一个 '.' 字符的位置
  const char* last_dot = strrchr(filename, '.');

  // 如果找到了 '.' 且不是第一个字符（避免隐藏文件被误判）
  if (last_dot != NULL && last_dot != filename) {
    // 计算扩展名长度（包含 '.'）
    size_t ext_len = strlen(last_dot);

    // 检查缓冲区大小是否足够
    if (ext_len >= size) {
      return -1;
    }

    // 使用安全的字符串复制函数
    snprintf(result, size, "%s", last_dot);
  } else {
    // 没有扩展名，返回空字符串
    if (size < 1) {
      return -1;
    }
    result[0] = '\0';
  }

  return 0;
}

/* get path dir */
losu_extern_t int32_t platfs_path_dir(const char* path,
                                      char* result,
                                      size_t size) {
  if (path == NULL || result == NULL || size == 0) {
    return -1;
  }

  // 查找路径中最后一个 '/' 字符的位置
  const char* last_slash = strrchr(path, '/');

  if (last_slash != NULL) {
    // 计算目录路径长度（包含最后的 '/'）
    size_t dir_len = last_slash - path + 1;

    // 检查缓冲区大小是否足够
    if (dir_len >= size) {
      return -1;
    }

    // 使用安全的字符串复制函数
    snprintf(result, size, "%.*s", (int)dir_len, path);
  } else {
    // 没有找到 '/'，表示当前目录，使用 "./" 格式
    if (size < 3) {
      return -1;
    }
    result[0] = '.';
    result[1] = '/';
    result[2] = '\0';
  }

  return 0;
}

#endif